/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.designschema.generator;

import com.inspur.edp.cef.designtime.api.collection.GspAssociationCollection;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.metadata.rtcustomization.api.CustomizationService;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.nocode.BusinessField;
import com.inspur.edp.udt.designtime.api.nocode.IBusinessFieldService;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.ResourceLocalizeUtil;
import com.inspur.edp.web.designschema.constant.I18nExceptionConstant;
import com.inspur.edp.web.designschema.constant.I18nMsgConstant;
import com.inspur.edp.web.designschema.elements.Field;
import com.inspur.edp.web.designschema.elements.SimpleField;
import com.inspur.edp.web.designschema.elements.type.*;
import com.inspur.edp.web.designschema.udtextensiondef.FormUdtExtension;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 字段类型 构造
 *
 * @author noah
 */
public class FieldTypeBuilder {
    private final FieldBuilder fieldBuilder;

    public FieldTypeBuilder(FieldBuilder fieldBuilder) {
        this.fieldBuilder = fieldBuilder;
    }

    public final FieldType GenerateFieldType(TypeBuildingContext context, TypeBuildingContext parentContext, String scene) {
        if (context.getHasUnifiedDataType()) {
            return GenerateObjectFieldType(context, parentContext, scene);
        }

        if (context.getHasBusiFieldId()) {
            return handleBusiField(context, parentContext, scene);
        }

        if (context.getHasAssociation()) {
            return GenerateEntityFieldType(context, parentContext, scene);
        }

        if (context.getObjectType() == GspElementObjectType.Enum) {
            return GenerateEnumFieldType(context);
        }

        if (context.getIsDynamicField()) {
            return GenerateDynamicFieldType(context);
        }

        return GenerateSimpleFieldType(context);
    }

    private EntityType handleBusiField(TypeBuildingContext context, TypeBuildingContext parentContext, String scene) {
        BusinessField businessField = getBusinessField(context);
        if (businessField == null) {
            throw new WebCustomException(I18nExceptionConstant.WEB_DESIGN_SCHEMA_ERROR_0009, new String[]{context.getBusinessFieldId()});
        }
        // 获取自定义业务字段扩展信息
        FormUdtExtension businessFiledInfo = (FormUdtExtension) businessField.getUnifiedDataTypeDef().getUdtExtensions().get("Form");
        // 自定以业务字段扩展信息塞入字段
        context.getParams().put("ExtendBusiFieldProp", businessFiledInfo);
        // 业务字段中含有关联字段，走条件为context.getHasAssociation()逻辑
        return GenerateEntityFieldType(context, parentContext, scene);
    }

    private FieldType GenerateSimpleFieldType(TypeBuildingContext context) {
        switch (context.getDataType()) {
            case Boolean:
                return new BooleanType();
            case Date:
                return new DateType();
            case DateTime:
                return new DateTimeType();
            case Decimal:
            case Integer:
                if (context.<Boolean>Get("IsBigNumber", Boolean.class)) {
                    BigNumericType tempVar = new BigNumericType();
                    tempVar.setLength(context.<Integer>Get("Length", Integer.class));
                    tempVar.setPrecision(context.<Integer>Get("Precision", Integer.class));
                    return tempVar;
                }
                NumericType tempVar2 = new NumericType();
                tempVar2.setLength(context.<Integer>Get("Length", Integer.class));
                tempVar2.setPrecision(context.<Integer>Get("Precision", Integer.class));
                return tempVar2;
            case String:
                StringType tempVar3 = new StringType();
                tempVar3.setLength(context.<Integer>Get("Length", Integer.class));
                return tempVar3;
            case Text:
                TextType tempVar4 = new TextType();
                tempVar4.setLength(context.<Integer>Get("Length", Integer.class));
                return tempVar4;
            default:
                StringType tempVar5 = new StringType();
                tempVar5.setLength(context.<Integer>Get("Length", Integer.class));
                return tempVar5;
        }
    }

    private EnumType GenerateEnumFieldType(TypeBuildingContext context) {
        EnumType enumType = new EnumType();
        enumType.setValueType(GenerateSimpleFieldType(context));
        for (int i = 0; i < context.getEnums().size(); i++) {
            GspEnumValue item = context.getEnums().get(i);
            EnumItem tempVar = new EnumItem();
            tempVar.setValue(item.getValue());
            tempVar.setName(item.getName());
            tempVar.setDisabled(item.getEnumItemDisabled());
            enumType.getEnumValues().add(tempVar);
        }
        return enumType;
    }

    private EntityType GenerateEntityFieldType(TypeBuildingContext context, TypeBuildingContext parentContext, String scene) {
        GspAssociationCollection associations = context.getAssociations();
        if (associations == null || associations.size() == 0) {
            throw new WebCustomException(I18nExceptionConstant.WEB_DESIGN_SCHEMA_ERROR_0003, new String[]{context.Get("Name", String.class)});
        }
        TypeBuildingContext originalFieldContext = TypeBuildingContext.CreateSimpleTypeContextFromAssociation(context, parentContext);
        Field originalField = fieldBuilder.build(originalFieldContext, parentContext, scene);
        ArrayList<Field> typeFields = new ArrayList<>(Collections.singletonList(originalField));
        String typeName = "";
        String displayTypeName = "";
        for (GspAssociation association : context.getAssociations()) {
            typeName = association.getRefModelCode();
            displayTypeName = association.getRefModelName();

            List<Field> fields = association.getRefElementCollection()
                    .stream()
                    .map(e -> {
                        Field f = fieldBuilder.build(e, context, scene);
                        return f;
                    }).collect(Collectors.toList());
            typeFields.addAll(fields);
        }

        typeName = context.ReviseTypeName(originalField.getId(), typeName);
        EntityType tempVar = new EntityType();
        tempVar.setName(typeName);
        tempVar.setDisplayName(displayTypeName);
        tempVar.setPrimary(originalField.getLabel());
        tempVar.setFields(typeFields);
        return tempVar;
    }

    private ObjectType GenerateObjectFieldType(TypeBuildingContext context, TypeBuildingContext parentContext, String scene) {
        UnifiedDataTypeDef unifiedDataType = GetUnifiedDataType(context);
        List<Field> fields = new ArrayList<>();
        String typeName = "";
        String displayTypeName = "";
        if (unifiedDataType instanceof SimpleDataTypeDef) {
            SimpleDataTypeDef simpleDataType = (SimpleDataTypeDef) unifiedDataType;
            Field field = fieldBuilder.build(simpleDataType, context, scene);
            if (field instanceof SimpleField) {
                SimpleField simpleDataField = (SimpleField) field;
                // 单值UDT的必填属性以VO字段的必填属性为准，按就近原则，与UI界面近的属性覆盖远的属性。
                simpleDataField.setRequire(context.<Boolean>Get("Require", Boolean.class));
                // 单值UDT的只读属性以VO字段的必填属性为准，按就近原则，与UI界面近的属性覆盖远的属性。
                simpleDataField.setReadonly(context.<Boolean>Get("Readonly", Boolean.class));

            }

            typeName = context.ReviseTypeName(field.getId(), simpleDataType.getCode());
            displayTypeName = unifiedDataType.getName();
            fields.add(field);
        } else if (unifiedDataType instanceof ComplexDataTypeDef) {
            ComplexDataTypeDef complexDataType = (ComplexDataTypeDef) unifiedDataType;
            typeName = context.ReviseTypeName(context.Get("Id", String.class), complexDataType.getCode());
            displayTypeName = unifiedDataType.getName();

            fields = complexDataType.getElements()
                    .stream()
                    .map(e -> fieldBuilder.build(e, context, scene)).collect(Collectors.toList());

        } else {
            throw new WebCustomException(I18nExceptionConstant.WEB_DESIGN_SCHEMA_ERROR_0004, new String[]{context.getUnifiedDataType()});
        }
        FormUdtExtension formUdtExtension = (FormUdtExtension) unifiedDataType.getUdtExtensions().get("Form");
        context.getParams().put("ExtendProperty", formUdtExtension);

        if ("dbfbe55d-ba65-4a7f-a9d4-4f664ec6ec68".equals(unifiedDataType.getId()) ||
                "12be876c-368c-4262-88ab-4112688540b0".equals(unifiedDataType.getId())) {
            HierarchyType tempVar = new HierarchyType();
            tempVar.setName(typeName);
            tempVar.setDisplayName(displayTypeName);
            tempVar.setFields(fields);
            return tempVar;
        }
        ObjectType tempVar2 = new ObjectType();
        tempVar2.setName(typeName);
        tempVar2.setDisplayName(displayTypeName);
        tempVar2.setFields(fields);
        return tempVar2;
    }

    private UnifiedDataTypeDef GetUnifiedDataType(TypeBuildingContext context) {
        if (context.getUnifiedDataTypeDefInstance() != null) {
            // 增加此参数的目的是因为零代码传递对应的实例  不传递uri
            return context.getUnifiedDataTypeDefInstance();
        }
        String uri = context.getUnifiedDataType();
        UnifiedDataTypeDef unifiedDataType;
        GspMetadata typeMetadata = null;
        if (context.isRuntime()) {
            CustomizationService customizationService = SpringBeanUtils.getBean(CustomizationService.class);
            typeMetadata = customizationService.getMetadata(uri);
        } else {
            MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance(uri, null, MetadataTypeEnum.ViewModel);
            metadataGetterParameter.setTargetMetadataNotFoundMessage(ResourceLocalizeUtil.getString(I18nMsgConstant.WEB_DESIGN_SCHEMA_MSG_0004, uri));
            typeMetadata = MetadataUtility.getInstance().getMetadataWithDesign(metadataGetterParameter);
        }
        if (typeMetadata == null) {
            throw new WebCustomException(I18nExceptionConstant.WEB_DESIGN_SCHEMA_ERROR_0005, new String[]{uri});
        }
        unifiedDataType = (UnifiedDataTypeDef) ((typeMetadata.getContent() instanceof UnifiedDataTypeDef) ? typeMetadata.getContent() : null);
        return unifiedDataType;
    }

    private BusinessField getBusinessField(TypeBuildingContext  context) {
        if (context.getBusinessFieldInstance() != null) {
            return context.getBusinessFieldInstance();
        }
        IBusinessFieldService businessFieldService = SpringBeanUtils.getBean(IBusinessFieldService.class);
        return businessFieldService.getBusinessField(context.getBusinessFieldId());
    }

    private DynamicObjectType GenerateDynamicFieldType(TypeBuildingContext context) {
        return new DynamicObjectType();
    }
}
